/*
日志器模块的实现：
    1. 抽象日志器基类
    2. 派生出同步日志器 和 异步日志器
*/

#include "Level.hpp"
#include "Sink.hpp"
#include "Formatter.hpp"
#include "Looper.hpp"

#include <stdlib.h>
#include <stdarg.h>
#include <unordered_map>

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

namespace LogModule
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name, LogLevel::Level limit_level,
               Formatter::ptr formatter, std::vector<LogSink::ptr> sinks)
            : _logger_name(logger_name), _limit_level(limit_level),
              _formatter(formatter), _sinks(sinks.begin(), sinks.end()) {}
        // 获取日志器名称
        const std::string& GetLoggerName() { return _logger_name; }
        // 构造日志消息对象，并将日志消息按照格式格式化为字符串 -- 然后再在下面进行落地 && 同步/异步操作
        // 至少需要提供：file 文件名   line 行号   format 格式   ... 有效载荷，对应前面的格式
        //                               格式和有效载荷对应说明：%s - %d 对应 "有效载荷" - 111
        void Debug(const std::string &file, size_t line, const std::string format, ...)
        {
            // 通过传入的参数，构造一个日志消息对象，并将消息进行格式化，最终落地
            // 1. 判断当前的日志是否满足日志输出等级
            if (LogLevel::Level::DEBUG < _limit_level)
                return;
            // 2. 走到这，说明可以进行日志输出操作。先构建日志消息对象
            // 因为传入的数据是可变参数，所以通过特定的格式，所以需要获取有效载荷
            char *res;
            va_list ap;
            va_start(ap, format);
            int ret = vasprintf(&res, format.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error!\n";
                return;
            }
            va_end(ap);
            Serialize(line, file, LogLevel::Level::DEBUG, res);
            free(res);
        }
        void Info(const std::string &file, size_t line, const std::string format, ...)
        {
            // 通过传入的参数，构造一个日志消息对象，并将消息进行格式化，最终落地
            // 1. 判断当前的日志是否满足日志输出等级
            if (LogLevel::Level::INFO < _limit_level)
                return;
            // 2. 按照特定的格式，获取有效载荷
            char *res;
            va_list ap;
            va_start(ap, format);
            int ret = vasprintf(&res, format.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error!\n";
                return;
            }
            va_end(ap);
            Serialize(line, file, LogLevel::Level::INFO, res);
            free(res);
        }
        void Warn(const std::string &file, size_t line, const std::string format, ...)
        {
            // 通过传入的参数，构造一个日志消息对象，并将消息进行格式化，最终落地
            // 1. 判断当前的日志是否满足日志输出等级
            if (LogLevel::Level::WARN < _limit_level)
                return;
            // 2. 按照特定的格式，获取有效载荷
            char *res;
            va_list ap;
            va_start(ap, format);
            int ret = vasprintf(&res, format.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error!\n";
                return;
            }
            va_end(ap);
            Serialize(line, file, LogLevel::Level::WARN, res);
            free(res);
        }
        void Error(const std::string &file, size_t line, const std::string format, ...)
        {
            // 通过传入的参数，构造一个日志消息对象，并将消息进行格式化，最终落地
            // 1. 判断当前的日志是否满足日志输出等级
            if (LogLevel::Level::ERROR < _limit_level)
                return;
            // 2. 按照特定的格式，获取有效载荷
            char *res;
            va_list ap;
            va_start(ap, format);
            int ret = vasprintf(&res, format.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error!\n";
                return;
            }
            va_end(ap);
            Serialize(line, file, LogLevel::Level::ERROR, res);
            free(res);
        }
        void Fatal(const std::string &file, size_t line, const std::string format, ...)
        {
            // 通过传入的参数，构造一个日志消息对象，并将消息进行格式化，最终落地
            // 1. 判断当前的日志是否满足日志输出等级
            if (LogLevel::Level::FATAL < _limit_level)
                return;
            // 2. 按照特定的格式，获取有效载荷
            char *res;
            va_list ap;
            va_start(ap, format);
            int ret = vasprintf(&res, format.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf error!\n";
                return;
            }
            va_end(ap);
            Serialize(line, file, LogLevel::Level::FATAL, res);
            free(res);
        }

    protected:
        void Serialize(size_t line, const std::string &file, LogLevel::Level level, const std::string &payload)
        {
            LogMessage message(line, file, level, payload, _logger_name);
            // 3. 将日志信息进行格式化，格式化结果放到ss中
            std::stringstream ss;
            _formatter->Format(ss, message);
            // 4. 将格式化好的日志信息进行落地
            Log(ss.str().c_str(), ss.str().size());
        }
        // 同步、异步将日志进行落地操作
        virtual void Log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;
        std::string _logger_name; // 日志器名称
        // 每一个日志器在日志输出时，都需要不断判断日志等级
        // 防止冲突，将等级设置为原子的
        std::atomic<LogLevel::Level> _limit_level;
        std::vector<LogSink::ptr> _sinks; // 一个日志器可能有很多落地方向
        Formatter::ptr _formatter;        // 日志按照什么格式输出
    };

    // 日志器的同步操作实现
    class SyncLogger : public Logger
    {
    public:
        // 同步操作，必须提供：
        // logger_name 日志器名称   limit_level 限制等级
        // formatter 格式类         sinks 日志消息落地数组
        SyncLogger(const std::string &logger_name, LogLevel::Level limit_level,
                   Formatter::ptr formatter, std::vector<LogSink::ptr> sinks)
            : Logger(logger_name, limit_level, formatter, sinks) {}

    protected:
        void Log(const char *data, size_t len) override
        {
            // 构造时自动锁定，析构时自动解锁 -- C++11提供
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            // 同步操作，只需要不断从sinks数组中提取需求，执行需求即可
            for (auto &sink : _sinks)
            {
                sink->Log(data, len);
            }
        }
    };

    // 异步日志器的实现
    class AsyncLogger : public Logger
    {
    public:
        // 同步操作，必须提供：
        // logger_name 日志器名称   limit_level 限制等级
        // formatter 格式类         sinks 日志消息落地数组
        // 默认为安全模式
        AsyncLogger(const std::string &logger_name, LogLevel::Level limit_level,
                    Formatter::ptr formatter, std::vector<LogSink::ptr> sinks,
                    AsyncType asynctype)
            : Logger(logger_name, limit_level, formatter, sinks),
              // bind的作用为，绑定一个个参数为this指针，因为类内函数有this指针，所以传参会报错
              // 绑定之后会形成一个新的函数，函数预留一个_1的参数位置，也就是buffer对象
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::RealLog, this, std::placeholders::_1), asynctype))
        {
        }

    protected:
        //  Log只需要将数据写到缓冲区
        void Log(const char *data, size_t len) override
        {
            _looper->Push(data, len);
        }
        // 实际的落地操作，只需要将数据向不同的落地方向，落地即可
        void RealLog(Buffer &buffer)
        {
            // 该操作不需要加锁，因为异步器内部在读取数据时会竞争锁
            // 所以操作时线程安全的！
            if (_sinks.empty())
                return; // 没有落地方向
            for (auto &sink : _sinks)
            {
                // 只需要向不同的落地方向写入即可
                sink->Log(buffer.Begin(), buffer.ReadAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    // 建造者模式，完成复杂对象的创建，设计思路：
    // 1. 抽象出一个日志器建造者类 -- 用于填写创建日志器需要的信息
    //   1.1 设置日志器需要的信息 -- 比如：日志器类型、日志器名称
    //   1.2 将日志器的创建工作安排到一个虚函数中，被派生类执行
    // 2. 派生出具体的建造者类 -- 局部日志建造者 & 全局日志建造正
    //  因为局部日志的建造和全局日志的建造很不相同，所以进行派生
    //  这里进行真正的局部条件下同步和异步日志器的创建(或者全局条件下)
    enum class LoggerType
    {
        LOGGER_SYNC, // 同步
        LOGGER_ASYNC // 异步
    };
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC),
              _limit_level(LogLevel::Level::DEBUG),
              _async_type(AsyncType::ASYNC_SAFE)
        {
        }
        using ptr = std::shared_ptr<LoggerBuilder>;
        // 用户使用时只需要传入必要的信息，创建过程由建造者实现
        // 必须传入的有：loggername 日志器名称
        void BulidLoggerType(LoggerType loggertype) { _logger_type = loggertype; }
        void BuildSetAsyncUnsafe() { _async_type = AsyncType::ASYNC_UNSAFE; }
        void BuildLoggerName(const std::string &loggername) { _logger_name = loggername; }
        void BuildLoggerLevel(LogLevel::Level loggerlevel) { _limit_level = loggerlevel; }
        void BuildLoggerFormatter(const std::string &format) { _formatter = std::make_shared<Formatter>(format); }

        template <typename SinkType, typename... Args>
        void BuildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::Create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }
        virtual Logger::ptr build() = 0;

    protected:
        LoggerType _logger_type;          // 同步/异步
        std::string _logger_name;         // 日志器名称
        LogLevel::Level _limit_level;     // 限制等级
        Formatter::ptr _formatter;        // 以什么格式输出
        std::vector<LogSink::ptr> _sinks; // 落地方向
        AsyncType _async_type;            // 安全/非安全模式
    };
    // 派生出具体的建造者类 -- 局部日志器的建造者 & 全局日志器的建造者
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        // 有了构建日志器需要的信息 -- LoggerBuilder中
        // 在这里进行具体的日志器创建！(局部下，同步/异步日志器创建)
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); // 必须要有日志器名称，因为要定位日志器
            // 不传日志格式，使用默认日志格式：[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n
            if (_formatter == nullptr)
                _formatter = std::make_shared<Formatter>();
            // 不传落地方向，默认向标准输出打印
            if (_sinks.empty())
            {
                LogSink::ptr psink = SinkFactory::Create<StdOutLogSink>();
                _sinks.push_back(psink);
            }
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _async_type);
            }
            else
            {
                // 不传_logger_type，默认为同步打印
                return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
        }
    };

    // 日志器管理器
    class LoggerManager
    {
    public:
        // 获取单例对象
        static LoggerManager& GetInstance() 
        {
            // C++11下，针对静态局部对象，编译器在编译的层面实现了线程安全
            // 当静态局部对象没有构造完成，其它线程会阻塞
            // 所以这里不存在线程安全的问题
            static LoggerManager eton;
            return eton;
        }
        // 增加日志器
        void AddLogger(Logger::ptr& logger) 
        {
            // 1. 先申请锁
            std::unique_lock<std::mutex> lock(_mutex);
            // 2. 再进行插入操作
            _loggers.insert(std::make_pair(logger->GetLoggerName(), logger));
        }
        // 通过日志器名称，查找该日志器是否存在
        bool HasLogger(const std::string name) 
        {
            // 查找判断过程，也要加锁
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return false;
            return true;
        }
        // 通过日志器名称，获取对应的日志器
        Logger::ptr GetLogger(const std::string name) 
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return Logger::ptr();
            return it->second;
        }
        // 获取默认日志器，默认日志器向标准输出打印
        // 如果我就是只想向标准输出打印，就可以直接使用，简化了用户操作
        Logger::ptr RootLogger() 
        {
            return _root_logger;
        }
    private:
        LoggerManager() 
        {
            // 工厂实现时，默认为：同步日志、限制等级为DEBUG、先标准输出打印
            std::shared_ptr<LoggerBuilder> builder = std::make_shared<LocalLoggerBuilder>();
            // 必须要传入的为日志器名称，默认的日志器为root日志器
            builder->BuildLoggerName("root");
            _root_logger = builder->build();
        }
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger; // 日志管理器中，默认有一个标准输出打印的日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    // 派生出具体的建造者类 -- 局部日志器的建造者 & 全局日志器的建造者
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); 
            // 不传日志格式，使用默认日志格式：[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n
            if (_formatter == nullptr)
                _formatter = std::make_shared<Formatter>();
            // 不传落地方向，默认向标准输出打印
            if (_sinks.empty())
            {
                LogSink::ptr psink = SinkFactory::Create<StdOutLogSink>();
                _sinks.push_back(psink);
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger =  std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _async_type);
            }
            else
            {
                // 不传_logger_type，默认为同步打印
                if(_logger_name.empty())  _logger_name = "root";
                logger =  std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::GetInstance().AddLogger(logger);
            return logger;
        }
    };
}

#endif